﻿using System;
using System.Threading;
using System.Linq.Expressions;

namespace Syncer
{
	public abstract class AsyncOperation<TResult> : IAsyncResult, IDisposable
	{
		#region fields

		private TResult _operationResult;
		private Exception _operationError;
		private AsyncCallback _asyncCallback;
		private bool _isCompleted;

		#endregion

		#region construction

		public AsyncOperation(AsyncCallback asyncCallback)
		{
			AsyncWaitHandle = new ManualResetEvent(false);
			_asyncCallback = asyncCallback;
			_isCompleted = false;
		}

		#endregion

		#region IAsyncResult

		public object AsyncState
		{
			get;
			private set;
		}

		public WaitHandle AsyncWaitHandle
		{
			get;
			private set;
		}

		public bool CompletedSynchronously
		{
			get
			{ return false; }
		}

		public bool IsCompleted
		{
			get
			{
				return _isCompleted;
			}
		}

		#endregion

		#region IDisposable

		~AsyncOperation()
		{
			Dispose(false);
		}

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected void Dispose(bool disposing)
		{
			if (disposing)
			{
				AsyncWaitHandle.Dispose();
			}
		}

		#endregion

		/// <summary>
		/// class should implement as a wrapper to the parameterized call of the function
		/// </summary>
		/// <returns></returns>
		protected abstract TResult OperationWrapper();

		public void BeginAsyncOperation()
		{
			ThreadPool.QueueUserWorkItem((x) =>
			{
				try
				{
					_operationResult = OperationWrapper();
				}
				catch (Exception ex)
				{
					_operationError = ex;
				}
				finally
				{
					(AsyncWaitHandle as ManualResetEvent).Set();

					if (_asyncCallback != null)
					{ _asyncCallback(this); }
				}
			});
		}

		public TResult EndAsyncOperation()
		{
			AsyncWaitHandle.WaitOne();
			_isCompleted = true;

			// if there was an error throw it.
			if (_operationError != null)
			{ throw _operationError; }

			return _operationResult;
		}
	}
}
